/*
 * ﻿Copyright (C) 2009-2013 NewMain Softech
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package com.newmainsoftech.gwetons.client;

import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject;
import com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap;
import com.newmainsoftech.gwetons.client.logging.JsniLoggerWrapFactory;
import com.newmainsoftech.gwetons.client.logging.JsniLoggerWrapFactoryImpl;
import com.newmainsoftech.gwetons.shared.logging.LoggerWrap;

public class RegExpJsniImpl implements RegExpJsni {
	// For logging --------------------------------------------------------------------------------
	public static String getNameForLogging() {
		return RegExpJsniImpl.class.getName();
	}
	
	/* Synchronization over jsniLoggerWrapFactory member field is not necessary since 
	 * JavaScript is single thread environment.
	 */
	private static JsniLoggerWrapFactory jsniLoggerWrapFactory;
		protected static JsniLoggerWrapFactory getJsniLoggerWrapFactory() {
			return jsniLoggerWrapFactory;
		}
		protected static void setJsniLoggerWrapFactory( JsniLoggerWrapFactory jsniLoggerWrapFactory) {
			RegExpJsniImpl.jsniLoggerWrapFactory = jsniLoggerWrapFactory;
			setLoggerWrap( null);
			setJsniLoggerWrap( null);
		}

	/* Synchronization over loggerWrap member field is not necessary since 
	 * JavaScript is single thread environment.
	 */
	private static LoggerWrap loggerWrap;
		protected static void setLoggerWrap( LoggerWrap loggerWrap) {
			RegExpJsniImpl.loggerWrap = loggerWrap;
			setJsniLoggerWrap( null);
		}
		private static LoggerWrap getLoggerWrap() {
			return RegExpJsniImpl.loggerWrap;
		}
	/* Synchronization over jsniLoggerWrap member field is not necessary since 
	 * JavaScript is single thread environment.
	 */
	private static JsniLoggerWrap jsniLoggerWrap;
		protected static JsniLoggerWrap getJsniLoggerWrap() {
			if ( jsniLoggerWrap == null) {
				setJsniLoggerWrap( 
						JsniLoggerWrapFactoryImpl.getJsniLoggerWrap(  
								null, getLoggerWrap(), getNameForLogging(), getJsniLoggerWrapFactory())
						);
			}
			return jsniLoggerWrap;
		}
		protected static void setJsniLoggerWrap( JsniLoggerWrap jsniLoggerWrap) {
			RegExpJsniImpl.jsniLoggerWrap = jsniLoggerWrap;
		}
	// --------------------------------------------------------------------------------------------
		
	/**
	 * @param regExpStr
	 * @param ignoreCase
	 * @param global
	 * @param multiline Treat beginning and end characters (^ and $) as working over multiple lines. <br /> 
	 * (i.e., match the beginning or end of each line (delimited by \n or \r), not only the very beginning 
	 * or end of the whole input string)
	 * @return JavaScriptObject object of JavaScript RegExp instance. null when regExpStr input is null. 
	 * @throws JavaScriptException 
	 */
	public static native JavaScriptObject getRegExp( 
			String regExpStr, boolean ignoreCase, boolean global, boolean multiline) 
	throws JavaScriptException /*-{
		// Preparations for logging ---------------------------------------------------------------
		var methodName = "getRegExp JSNI";
		var thisJsniLoggerWrap = @com.newmainsoftech.gwetons.client.RegExpJsniImpl::getJsniLoggerWrap()();
		var jsniLogEntering
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
		var jsniLogging
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
		var jsniLogResult
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
		// ----------------------------------------------------------------------------------------
		
		jsniLogEntering( 
			methodName, 
			[ "regExpStr (=" + String( regExpStr) + ")", "ignoreCase (=" + String( ignoreCase) + ")", 
				"global (=" + String( global) + ")", "multiline (=" + String( multiline) + ")" 
			]
			);
		
		var regExpFlagStr = "";
		if ( global) regExpFlagStr = regExpFlagStr + "g";
		if ( ignoreCase) regExpFlagStr = regExpFlagStr + "i";
		if ( multiline) regExpFlagStr = regExpFlagStr + "m";

		var regExpObj = null;
		if ( typeof( regExpStr) == "string") {
			try {
				if ( regExpFlagStr.length > 0) {
					regExpObj = new RegExp( regExpStr, regExpFlagStr);
				}
				else {
					regExpObj = new RegExp( regExpStr);
				}
			}
			catch( err) {
				jsniLogging( @java.util.logging.Level::WARNING, 
					String( err) + " \nOccurred by these arguments: " 
					+ "\n\tregExpStr = \"" + String( regExpStr) + "\", "
					+ "\n\tignoreCase = " + String( ignoreCase) + ", "
					+ "\n\tglobal = " + String( global) + ", "
					+ "\n\tmultiline = " + String( multiline)
					);
				throw err;
			}
		}

		jsniLogResult( methodName, String( regExpObj));
		return regExpObj;
	}-*/;
	
	/**
	 * @param regExpStr
	 * @param ignoreCase
	 * @param global
	 * @return JavaScriptObject of JavaScript RegExp instance 
	 * @throws JavaScriptException
	 */
	public static JavaScriptObject getRegExp( String regExpStr, boolean ignoreCase, boolean global) 
	throws JavaScriptException {
		return RegExpJsniImpl.getRegExp( regExpStr, ignoreCase, global, false);
	}
	
	private final JavaScriptObject regExpObj;
		// Do not make getter for regExpObj member field to avoid property values' changes out of control by this class.
	final String regExpStr;
	final boolean ignoreCase;
		public boolean getIgnoreCase() { // RegExpJsni interface method
			return this.ignoreCase;
		}
		public static native boolean getIgnoreCase( JavaScriptObject regExpObj) throws JavaScriptException /*-{
			// Preparations for logging ---------------------------------------------------------------
			var methodName = "getIgnoreCase JSNI";
			var thisJsniLoggerWrap = @com.newmainsoftech.gwetons.client.RegExpJsniImpl::getJsniLoggerWrap()();
			var jsniLogEntering
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
			var jsniLogging
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
			var jsniLogResult
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
			// ----------------------------------------------------------------------------------------
			
			jsniLogEntering( methodName, [ "regExpObj(= " + String( regExpObj) + ")"]);
				
			if ( 
				!@com.newmainsoftech.gwetons.client.RegExpJsniImpl::sanityCheckOnRegExpObj(Lcom/google/gwt/core/client/JavaScriptObject;)( 
					regExpObj)
					) 
			{
				var message = "Invalid value as regExpObj argument: " + String( regExpObj);
				jsniLogging( @java.util.logging.Level::WARNING, message);
				jsniLogResult( methodName);
				throw message;
			}
			
			var ignoreCase = regExpObj.ignoreCase;
			jsniLogResult( methodName, String( ignoreCase));
			return ignoreCase;
		}-*/;
	final boolean global;
		public boolean getGlobal() { // RegExpJsni interface method
			return this.global;
		};
		public static native boolean getGlobal( JavaScriptObject regExpObj) throws JavaScriptException /*-{
			// Preparations for logging ---------------------------------------------------------------
			var methodName = "getGlobal JSNI";
			var thisJsniLoggerWrap = @com.newmainsoftech.gwetons.client.RegExpJsniImpl::getJsniLoggerWrap()();
			var jsniLogEntering
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
			var jsniLogging
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
			var jsniLogExiting
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingFunction()();
			var jsniLogResult
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
			// ----------------------------------------------------------------------------------------
			
			jsniLogEntering( methodName, [ "regExpObj(= " + String( regExpObj) + ")"]);
				
			if ( 
				!@com.newmainsoftech.gwetons.client.RegExpJsniImpl::sanityCheckOnRegExpObj(Lcom/google/gwt/core/client/JavaScriptObject;)( 
					regExpObj)
					) 
			{ 
				var message = "Invalid value as regExpObj argument: " + String( regExpObj);
				jsniLogging( @java.util.logging.Level::WARNING, message);
				jsniLogExiting( methodName);
				throw message;
			}
			
			var global = regExpObj.global;
			jsniLogResult( methodName, String( global));
			return global;
		}-*/;
	/**
	 * Treat beginning and end characters (^ and $) as working over multiple lines 
	 * (i.e., match the beginning or end of each line (delimited by \n or \r), not only 
	 * the very beginning or end of the whole input string)
	 */
	final boolean multiline;
		public boolean getMultiline() { // RegExpJsni interface method
			return this.multiline;
		};
		public static native boolean getMultiline( JavaScriptObject regExpObj) throws JavaScriptException /*-{
			// Preparations for logging ---------------------------------------------------------------
			var methodName = "getMultiline JSNI";
			var thisJsniLoggerWrap = @com.newmainsoftech.gwetons.client.RegExpJsniImpl::getJsniLoggerWrap()();
			var jsniLogEntering
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
			var jsniLogging
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
			var jsniLogExiting
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingFunction()();
			var jsniLogResult
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
			// ----------------------------------------------------------------------------------------
			
			jsniLogEntering( methodName, [ "regExpObj(= " + String( regExpObj) + ")"]);
				
			if ( 
				!@com.newmainsoftech.gwetons.client.RegExpJsniImpl::sanityCheckOnRegExpObj(Lcom/google/gwt/core/client/JavaScriptObject;)( 
					regExpObj)
				) 
			{
				var message = "Invalid value as regExpObj argument: " + String( regExpObj);
				jsniLogging( @java.util.logging.Level::WARNING, message);
				jsniLogExiting( methodName);
				throw message;
			}
			
			var multiline = regExpObj.multiline;
			jsniLogResult( methodName, String( multiline));
			return multiline;
		}-*/;
		
	// About source property
		public static native String getSource( JavaScriptObject regExpObj) throws JavaScriptException /*-{
			// Preparations for logging ---------------------------------------------------------------
			var methodName = "getSource JSNI";
			var thisJsniLoggerWrap = @com.newmainsoftech.gwetons.client.RegExpJsniImpl::getJsniLoggerWrap()();
			var jsniLogEntering
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
			var jsniLogging
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
			var jsniLogExiting
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingFunction()();
			var jsniLogResult
			= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
			// ----------------------------------------------------------------------------------------
			
			jsniLogEntering( methodName, [ "regExpObj(= " + String( regExpObj) + ")"]);
			
			if ( 
				!@com.newmainsoftech.gwetons.client.RegExpJsniImpl::sanityCheckOnRegExpObj(Lcom/google/gwt/core/client/JavaScriptObject;)( 
					regExpObj)
				) 
			{
				var message = "Invalid value as regExpObj argument: " + String( regExpObj);
				jsniLogging( @java.util.logging.Level::WARNING, message);
				jsniLogExiting( methodName);
				throw message;
			}
			
			var source = regExpObj.source;
			jsniLogResult( methodName, String( source));
			return source;
		}-*/;
		
		public String getSource() { // // RegExpJsni interface method
			return RegExpJsniImpl.getSource( regExpObj);
		}
	
	public RegExpJsniImpl( String regExpStr, boolean ignoreCase, boolean global, boolean multiline) {
		this.regExpObj = RegExpJsniImpl.getRegExp( regExpStr, ignoreCase, global, multiline);
		this.regExpStr = regExpStr;
		this.ignoreCase = ignoreCase;
		this.global = global;
		this.multiline = multiline;
	}
	
	protected static native boolean sanityCheckOnRegExpObj( JavaScriptObject regExpObj) 
	throws JavaScriptException /*-{
		// Preparations for logging ---------------------------------------------------------------
		var methodName = "sanityCheckOnRegExpObj JSNI";
		var thisJsniLoggerWrap = @com.newmainsoftech.gwetons.client.RegExpJsniImpl::getJsniLoggerWrap()();
		var jsniLogEntering
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
		var jsniLogging
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
		var jsniLogResult
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
		// ----------------------------------------------------------------------------------------
		
		jsniLogEntering( methodName, [ "regExpObj(=" + String( regExpObj) + ")"]);
		
		if ( ( typeof regExpObj) != "object") {
			jsniLogging(
				@java.util.logging.Level::INFO,
				"Invalid value of regExpObj argument as a RegExp JavaScript instance: "
				+ "typeof( regExpObj) = " + typeof( regExpObj)
				);
			jsniLogResult( methodName, false);				
			return false;
		}
		if ( regExpObj == null) {
			jsniLogging(
				@java.util.logging.Level::INFO,
				"Invalid value of regExpObj argument as a RegExp JavaScript instance: null"
				);
			jsniLogResult( methodName, false);				
			return false;
		}
		
		if ( RegExp !== regExpObj.constructor) {
			jsniLogging(
				@java.util.logging.Level::INFO,
				"The constructor of regExpObj argument is not one of RegExp object: " 
				+ String( regExpObj.constructor)
				);
			jsniLogResult( methodName, false);				
			return false;
		}
		
		// It is acceptable to check all properties and methods of a RegExp instance, since 
		// those being checked below are implemented by JavaScript ver 1.2.
		var message 
		= "Invalid value of regExpObj argument as a RegExp JavaScript instance: " + String( regExpObj);
		
		if ( typeof ( regExpObj.test) != "function") {
			jsniLogging( @java.util.logging.Level::INFO, message);
			jsniLogResult( methodName, false);				
			return false;
		}
		if ( typeof ( regExpObj.exec) != "function") {
			jsniLogging( @java.util.logging.Level::INFO, message);
			jsniLogResult( methodName, false);				
			return false;
		}
		if ( typeof ( regExpObj.global) != "boolean") {
			jsniLogging( @java.util.logging.Level::INFO, message);
			jsniLogResult( methodName, false);				
			return false;
		}
		if ( typeof ( regExpObj.ignoreCase) != "boolean") {
			jsniLogging( @java.util.logging.Level::INFO, message);
			jsniLogResult( methodName, false);				
			return false;
		}
		if ( typeof ( regExpObj.lastIndex) != "number") {
			jsniLogging( @java.util.logging.Level::INFO, message);
			jsniLogResult( methodName, false);				
			return false;
		}
		if ( typeof ( regExpObj.source) != "string") {
if ( @com.google.gwt.core.client.GWT::isScript()) { // RegExp object on HtmlUnit doesn't have source property
			jsniLogging( @java.util.logging.Level::INFO, message);
			jsniLogResult( methodName, false);				
			return false;
}
		}
		if ( typeof ( regExpObj.multiline) != "boolean") {
			jsniLogging( @java.util.logging.Level::INFO, message);
			jsniLogResult( methodName, false);				
			return false;
		}
		
		jsniLogResult( methodName, true);				
		return true;
	}-*/;
	
	/**
	 * @param inputStr Should be single line string but multiple-line string.
	 * @param regExp Regular expression string being fed to RegExp constructor.
	 * @param ignoreCase
	 * @return index value points the character FOLLOWING the regExpObj's pattern matched last time. <br /> 
	 * Thereby, please remind that this will return 0 when the pattern of regExpObj was not found in 
	 * the last operation on regExpObj input.  
	 */
	public static native int lastIndex( String inputStr, String regExp, boolean ignoreCase) 
	throws JavaScriptException /*-{
		// Preparations for logging ---------------------------------------------------------------
		var methodName = "lastIndex JSNI";
		var thisJsniLoggerWrap = @com.newmainsoftech.gwetons.client.RegExpJsniImpl::getJsniLoggerWrap()();
		var jsniLogEntering
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
		var jsniLogging
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
		var jsniLogExiting
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingFunction()();
		var jsniLogResult
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
		// ----------------------------------------------------------------------------------------
		
		jsniLogEntering( 
			methodName, [ "inputStr(= " + String( inputStr) + ")", "regExp(" + String( regExp) + ")"]);
		
		var isSubstantialStrReferrer 
		= @com.newmainsoftech.gwetons.client.JavaScriptObjectUtil::isSubstantialStr(Ljava/lang/String;);
		
		if ( !isSubstantialStrReferrer( inputStr)) {
			var message = "Invalid value of inputStr argument: " + String( inputStr);
			jsniLogging( @java.util.logging.Level::WARNING, message);
			jsniLogExiting( methodName);
			throw message;
		}
		if ( !isSubstantialStrReferrer( regExp)) {
			var message = "Invalid value of regExp argument: " + String( regExp);
			jsniLogging( @java.util.logging.Level::WARNING, message);
			jsniLogExiting( methodName);
			throw message;
		}

		var regExpFlagStr = "g";
		if ( ( typeof ignoreCase) == "boolean") {
			if ( ignoreCase) regExpFlagStr = regExpFlagStr + "i";
		}

		var regExpObj = new RegExp( regExp, regExpFlagStr);
		regExpObj.test( inputStr)
		var lastIndex = regExpObj.lastIndex;
		
		jsniLogResult( methodName, String( lastIndex));
		return lastIndex;
	}-*/;
	
	/**
	 * @param regExpObj
	 * @return index value points the character FOLLOWING the regExpObj's pattern matched last time. 
	 * Thereby, please remind that this will return 0 when the pattern of regExpObj was not found in 
	 * the last operation on regExpObj input. -1 when global flag of regExpObj has not been set. 
	 */
	public static native int getLastIndex( JavaScriptObject regExpObj) throws JavaScriptException /*-{
		// Preparations for logging ---------------------------------------------------------------
		var methodName = "getLastIndex JSNI";
		var thisJsniLoggerWrap = @com.newmainsoftech.gwetons.client.RegExpJsniImpl::getJsniLoggerWrap()();
		var jsniLogEntering
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
		var jsniLogging
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
		var jsniLogExiting
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingFunction()();
		var jsniLogResult
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
		// ----------------------------------------------------------------------------------------
		
		jsniLogEntering( methodName, [ "regExpObj (= " + String( regExpObj) + ")"]);
		
		if ( 
			!@com.newmainsoftech.gwetons.client.RegExpJsniImpl::sanityCheckOnRegExpObj(Lcom/google/gwt/core/client/JavaScriptObject;)( 
				regExpObj)
			) 
		{
			var message = "Invalid value as regExpObj argument: " + String( regExpObj);
			jsniLogging( @java.util.logging.Level::WARNING, message);
			jsniLogExiting( methodName);
			throw message;
		}
		if ( !regExpObj.global) {
			var message = "global property of regExpObj argument is not set.";
			jsniLogging( @java.util.logging.Level::WARNING, message);
			jsniLogResult( methodName, -1);
			return -1;
		}
		
		var lastIndex = regExpObj.lastIndex;
		jsniLogResult( methodName, String( lastIndex));
		return lastIndex;
	}-*/;
	
	/**
	 * @return index value points the character FOLLOWING the regExpObj's pattern matched last time. 
	 * Thereby, please remind that this will return 0 when the pattern of regExpObj was not found in 
	 * the last operation on regExpObj input. -1 when global flag of regExpObj has not been set. 
	 */
	public int getLastIndex() { // RegExpJsni interface method
		if ( !getGlobal()) return -1; 
		
		return RegExpJsniImpl.getLastIndex( regExpObj);
	}

	String input = null;
		/*
		 * @param input cannot be null.
		 */
		void setInput( String inputStr) {
			if ( inputStr == null) {
				throw new IllegalArgumentException( "The value of input argument cannot be null.");
			}
			this.input = inputStr;
		}
		/**
		 * Please be advised that the return value of getInput method is unique for 
		 * each RegExpJsni instance, not like value of deprecated input property of global 
		 * JavaScript RegExp object. 
		 * @return string against what a regular expression was matched. Null when there was 
		 * no match so far.
		 */
		public String getInput() { // RegExpJsni interface method
			return this.input;
		}
		/**
		 * @deprecated Use getInput method instead.
		 * @return input property value of global JavaScript RegExp object. 
		 */
		public static native String getInputProperty() /*-{
			return RegExp.input;
		}-*/;
	
	/**
	 * @deprecated Use getLeftContext method instead.
	 * @return leftContext property value of global JavaScript RegExp object. 
	 */
	public static native String getLeftContextProperty() /*-{
		return RegExp.leftContext;
	}-*/;

	String leftContext = "";
		/**
		 * Please be advised that the return value of getLeftContext method is unique for 
		 * each RegExpJsni instance, not like value of deprecated leftContext property of global 
		 * JavaScript RegExp object. 
		 * @return substring preceding the most recent match out of string having processed by test 
		 * method or exec method.  
		 */
		public String getLeftContext() { // RegExpJsni interface method
			// leftContext could be obtained with JavaScript RegExp object regardless global property
			
			return leftContext;
		}
		void setLeftContext( String leftContext) {
			this.leftContext = leftContext;
		}

	/**
	 * @deprecated Use getRightContext method instead.
	 * @return rightContext property value of global JavaScript RegExp object. 
	 */
	public static native String getRightContextProperty() /*-{
		return RegExp.rightContext;
	}-*/;

	String rightContext = "";
		/**
		 * Please be advised that the return value of getLeftContext method is unique for 
		 * each RegExpJsni instance, not like value of deprecated leftContext property of global 
		 * JavaScript RegExp object. 
		 * @return
		 */
		public String getRightContext() { // RegExpJsni interface method
			// rightContext could be obtained with JavaScript RegExp object regardless global property
			
			return rightContext;
		}
		
		void setRightContext( String rightContext) {
			this.rightContext = rightContext;
		}
	
	void setBothSidesContext( String leftContext, String rightContext) {
		this.leftContext = leftContext;
		this.rightContext = rightContext;
	}
	
	String lastMatch = null;
		void setLastMatch(String lastMatch) {
			this.lastMatch = lastMatch;
		}
		/**
		 * @return (last) matched text out of string having processed by test method or exec method.  
		 * null when there was no match.
		 */
		public String getLastMatch() { // RegExpJsni interface method
			// lastMatch could be obtained with JavaScript RegExp object regardless global property
			
			return lastMatch;
		}
		/**
		 * @deprecated Use getLastMatch method instead.
		 * @return
		 */
		public static native String getLastMatchProperty() /*-{
			return RegExp.lastMatch;
		}-*/;
		
	public boolean test( String inputStr) { // RegExpJsni interface method
		String methodName = "test";
		LoggerWrap loggerWrap = RegExpJsniImpl.getJsniLoggerWrap().getLoggerWrap(); 
		
		loggerWrap.entering( 
				methodName,
				new Object[]{ "inputStr(=" + ((inputStr==null) ? "null" : inputStr) + ")"}
				);
		
		if ( !JavaScriptObjectUtil.isSubstantialStr( inputStr)) {
			loggerWrap.exiting( methodName, false);
			return false;
		}
		
		String matchedStr = exec( inputStr);
		if ( matchedStr == null) {
			loggerWrap.exiting( methodName, false);
			return false;
		}
		
		loggerWrap.exiting( methodName, true);
		return true; 
	}

	/**
	 * @param regExpObj
	 * @param inputStr
	 * @return
	 */
	public static native boolean test( JavaScriptObject regExpObj, String inputStr) /*-{
		// Preparations for logging ---------------------------------------------------------------
		var methodName = "test JSNI";
		var thisJsniLoggerWrap = @com.newmainsoftech.gwetons.client.RegExpJsniImpl::getJsniLoggerWrap()();
		var jsniLogEntering
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
		var jsniLogging
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
		var jsniLogResult
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
		// ----------------------------------------------------------------------------------------
		
		jsniLogEntering( 
			methodName, 
			[ "regExpObj (=" + String( regExpObj) + ")", "inputStr (= " + String( inputStr) + ")"]
			);
		
		if ( 
			!@com.newmainsoftech.gwetons.client.RegExpJsniImpl::sanityCheckOnRegExpObj(Lcom/google/gwt/core/client/JavaScriptObject;)( 
				regExpObj)
			) 
		{
			var message = "Invalid value as regExpObj argument: " + String( regExpObj);
			jsniLogging( @java.util.logging.Level::WARNING, message);
			jsniLogResult( methodName, false);
			return false;
		}
		if ( 
			!@com.newmainsoftech.gwetons.client.JavaScriptObjectUtil::isSubstantialStr(Ljava/lang/String;)( 
				inputStr)
			) 
		{
			var message = "Invalid value of inputStr argument: " + String( inputStr);
			jsniLogging( @java.util.logging.Level::WARNING, message);
			jsniLogResult( methodName, false);
			return false;
		}
		
		var testResult = regExpObj.test( inputStr);
		jsniLogResult( methodName, testResult);
		return testResult;
	}-*/;
	
	public static native String exec( JavaScriptObject regExpObj, String inputStr) throws JavaScriptException /*-{
		var methodName = "exec JSNI";
		// Preparations for logging ---------------------------------------------------------------
		var methodName = "test JSNI";
		var thisJsniLoggerWrap = @com.newmainsoftech.gwetons.client.RegExpJsniImpl::getJsniLoggerWrap()();
		var jsniLogEntering
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getEnteringFunction()();
		var jsniLogging
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getLogFunction()();
		var jsniLogExiting
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingFunction()();
		var jsniLogResult
		= thisJsniLoggerWrap.@com.newmainsoftech.gwetons.client.logging.JsniLoggerWrap::getExitingWithResultFunction()();
		// ----------------------------------------------------------------------------------------
		
		jsniLogEntering( 
			methodName, 
			[ "regExpObj (=" + String( regExpObj) + ")", "inputStr (= " + String( inputStr) + ")"]
			);
		
		if ( 
			!@com.newmainsoftech.gwetons.client.RegExpJsniImpl::sanityCheckOnRegExpObj(Lcom/google/gwt/core/client/JavaScriptObject;)( 
				regExpObj)
			) 
		{
			var message = "Invalid value as regExpObj argument: " + String( regExpObj);
			jsniLogging( @java.util.logging.Level::WARNING, message);
			jsniLogExiting( methodName);
			throw message;
		}
		if ( 
			!@com.newmainsoftech.gwetons.client.JavaScriptObjectUtil::isSubstantialStr(Ljava/lang/String;)( 
				inputStr)
			) 
		{
			var message = "Invalid value of inputStr argument: " + inputStr;
			jsniLogging( @java.util.logging.Level::WARNING, message);
			jsniLogExiting( methodName);
			throw message;
		}
		
		var matchedStr = regExpObj.exec( inputStr);
			if ( matchedStr != null) matchedStr = matchedStr[ 0];
		
		jsniLogResult( methodName, matchedStr);
		return matchedStr;
	}-*/;
	
	public String exec( String inputStr) { // RegExpJsni interface method
		String methodName = "exec";
		LoggerWrap loggerWrap = RegExpJsniImpl.getJsniLoggerWrap().getLoggerWrap(); 
		
		loggerWrap.entering( 
				methodName, new Object[]{ "inputStr(=" + ((inputStr==null) ? "null" : inputStr) + ")"}
				);
		
		if ( !JavaScriptObjectUtil.isSubstantialStr( inputStr)) {
			loggerWrap.exiting( methodName, null);
			return null;
		}
		
		String matchedStr = RegExpJsniImpl.exec( this.regExpObj, inputStr);
		if ( matchedStr != null) {
			setLastMatch( matchedStr);
			if ( !inputStr.equals( getInput())) {
				setInput( inputStr);
			}
			
			int lastIndex = getLastIndex();	// returned value is from execution of above exec method
			if ( lastIndex < 0) { // global property is off
				JavaScriptObject regExpCopy 
				= RegExpJsniImpl.getRegExp( regExpStr, getIgnoreCase(), true, getMultiline());
				RegExpJsniImpl.test( regExpCopy, inputStr);
				lastIndex = RegExpJsniImpl.getLastIndex( regExpCopy);
			}
			
			String leftContext = inputStr.substring( 0, (lastIndex - matchedStr.length()));
			String rightContext = inputStr.substring( lastIndex);
			setBothSidesContext( leftContext, rightContext);
		}
		
		loggerWrap.exiting( methodName, matchedStr);
		return matchedStr; 
	}
}
